package com.eascs.spider.yuqing.common.util;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eascs.spider.yuqing.common.proj.HttpMethod;
/**
 * 下载器
 * @author hzp
 *
 */
public class Downloader {

	private static Logger logger = LoggerFactory.getLogger(Downloader.class);
	
	private CloseableHttpClient client;
	
	private Map<String, String> header;
	
	private Map<String, String> param;
	
	private String cookie;
	
	private HttpMethod httpMethod;
	
	private String url;
	
	private String html;//用于临时存放返回的html,每次请求都会覆盖原来的html
	
	public Downloader(){
		this.client = initHttpClient();
	}
	
	public static Downloader build(){
		return new Downloader();
	}
	
	private CloseableHttpClient initHttpClient(){
		BasicCookieStore cookieStore = new BasicCookieStore();
		return initHttpClient(cookieStore, false);
	}
	
	public Downloader setHttpMethod(HttpMethod httpMethod){
		this.httpMethod = httpMethod;
		return this;
	}
	
	public Downloader setUrl(String url){
		this.url = url;
		return this;
	}
	
	public Downloader setHeader(Map<String, String> header){
		this.header = header;
		if(header.get("Cookie") != null)
			this.cookie = header.get("Cookie");
		return this;
	}
	
	public Downloader setParam(Map<String, String> param){
		this.param = param;
		return this;
	}
	
	public String getHtml(){
		return html;
	}
	/**
	 * 
	 * 初始化httpClient
	 * 
	 * @param cookieStore	null则为默认
	 * @return CloseableHttpClient
	 */
	private CloseableHttpClient initHttpClient(CookieStore cookieStore ,boolean isProxy){
        SSLConnectionSocketFactory sslsf = null;
        RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.DEFAULT)
        		.setConnectTimeout(60000).setConnectionRequestTimeout(30000).setSocketTimeout(60000)
        		.build();
        try {
			SSLContext sslContext = new SSLContextBuilder()
                                .loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain,
                                String authType) throws CertificateException {
                    return true;
                }
                    }).build();
            sslsf = new SSLConnectionSocketFactory(sslContext,new String[]{"TLSv1"}, null,NoopHostnameVerifier.INSTANCE);
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        
		Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslsf)
                .build();
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(registry);
        
        
		HttpClientBuilder httpClientBuilder = HttpClients.custom()
				.setDefaultRequestConfig(globalConfig)
				.setDefaultCookieStore(cookieStore)
		        .setConnectionManager(poolingHttpClientConnectionManager)
		        .setSSLSocketFactory(sslsf);
		//判断是否需要代理，代理参数是否完整
		if (isProxy) {
	        //添加代理
			String proxyIp = "http-pro.abuyun.com";
			String proxyPort = "9010";
			String proxyUser = "H70B99V9Z752485P";
			String proxyPassword = "67CA67814D3A9E21";
			
			CredentialsProvider credsProvider = new BasicCredentialsProvider();
			credsProvider.setCredentials(new AuthScope(proxyIp, Integer.valueOf(proxyPort)), new UsernamePasswordCredentials(proxyUser, proxyPassword));
			
			//配置代理
	        HttpHost proxy = new HttpHost(proxyIp, Integer.valueOf(proxyPort),"http");
			httpClientBuilder.setProxy(proxy);
			httpClientBuilder.setDefaultCredentialsProvider(credsProvider);
		}
		CloseableHttpClient client = httpClientBuilder.build();
		
		return client;
	}
	
	public Downloader download(){
		HttpEntityEnclosingRequestBase request = new HttpEntityEnclosingRequestBase() {
			@Override
			public String getMethod() {
				return httpMethod.getMethod();
			}
		};
		//设置url
		request.setURI(URI.create(url));
		//设置默认请求头
		request.setHeader("Content-Type", "application/x-www-form-urlencoded");
		request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36");
		
		//设置外入请求头
		if(header!=null && !header.isEmpty()){
			for(String key:header.keySet()){
				request.setHeader(key, header.get(key));
			}
		}
		
		//设置GET请求参数
		if(request.getMethod().equals("GET")){
			if(param!=null && !param.isEmpty()){
	    		url = url + "?";
	    		for(String key:param.keySet()){
	    			url = url + key + "=" + param.get(key) + "&";
	    		}
	    		url = url.substring(0, url.length()-1);
	    	}
		}else{
			//设置post请求参数
			try {
				request.setEntity(new UrlEncodedFormEntity(packagePostParams(param) , "utf-8"));
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
		}
		
    	//开始请求
    	try {
    		CloseableHttpResponse response = client.execute(request);
        	//获取响应cookie,并更新全局header,以及cookie
        	updateCookie(response);
        	this.html = EntityUtils.toString(response.getEntity(), "utf-8");
        	EntityUtils.consumeQuietly(response.getEntity());
        	
		} catch (Exception e) {
			logger.error("download发生异常" ,e);
		}
    	return this;
	}
	
	private void updateCookie(CloseableHttpResponse response){
		StringBuilder sb = new StringBuilder();
		List<Header> updateHeaders = new ArrayList<>();
		for(Header header : response.getAllHeaders()){
    		if(header.getName().equals("Set-Cookie")){
    			for(String temp : cookie.split("; ")){
    				//响应头中的cookie name
    				String headerCookieNameTemp = header.getValue().substring(0, header.getValue().indexOf("="));
    				if(headerCookieNameTemp.equals(temp.split("=")[0])){
    					updateHeaders.add(header);
    					continue;
    				}else{
    					sb.append(temp).append("; ");
    				}
    			}
    		}
    	}
		for(Header header : updateHeaders){
			String headerCookieNameTemp = header.getValue().substring(0, header.getValue().indexOf("="));
			String headerCookieValTemp = header.getValue().substring(header.getValue().indexOf("=") +1 , header.getValue().indexOf(";"));
			logger.info("更新cookie,{}:{}" , headerCookieNameTemp , headerCookieValTemp);
			sb.append(headerCookieNameTemp).append("=").append(headerCookieValTemp).append("; ");
		}
		
		if(sb.toString().length() != 0){
			cookie = sb.toString();
			cookie = cookie.substring(0, cookie.length()-2);
			this.header.put("Cookie", cookie);
		}
	}
	
	//封装post参数
	private List<NameValuePair> packagePostParams(Map<String,String> postParams){
		List<NameValuePair> nvps = new ArrayList<NameValuePair>();
		if(postParams!=null){
			for(String key:postParams.keySet()){
				nvps.add(new BasicNameValuePair(key, postParams.get(key)));
			}
		}
		return nvps;
	}
	
	public static void main(String[] args) {
		String value ="b-beta2-config=%7B%22hasPhoneNum%22%3A%221%22%2C%22ecreate_time%22%3A%2220100413%22%2C%22v%22%3A%222%22%2C%22d%22%3A287%2C%22e%22%3A612512%2C%22ejm%22%3A%221%22%2C%22entry%22%3A%221%22%2C%22p%22%3A%220%22%2C%22n%22%3A%22%25E5%25BD%25AD%25E4%25B8%25BD%25E8%258A%25B3%22%2C%22audit%22%3A%221%22%2C%22ecomp_id%22%3A612512%2C%22jz%22%3A%220%22%2C%22version%22%3A%222%22%7D; Domain=.liepin.com; Expires=Thu, 30-Nov-2017 02:22:23 GMT; Path=/";
		System.out.println(value.substring(0, value.indexOf("=")));
		System.out.println(value.substring(value.indexOf("=")+1, value.indexOf(";")));
	}
}
